/**
 * Definition of temporal entities.
 *
 * \file temporal.hpp
 * \author tomby
 * \date 04-01-2009
 **/

/*  Copyright (C) 2009  Tomas 'tomby' Bily

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */

#ifndef __DBC_TEMPORAL__
#define __DBC_TEMPORAL__

namespace dbc {
  namespace temporal {

    template <typename type, typename position_type>
    class StampableCmp;

    /***
     * value - must be comparable and hold rules of strict weak ordering (ireflexive, transitive, antisymetrive)
     */
    template <typename type, typename position_type>
    class Stamp
    {
    public:
      typedef type Type;

    private:
      type		value;
      type		old_value;
      position_type	position;
      
    public:

      Stamp ()
      {}

      Stamp (type v)
	: value (v), old_value (v)
      {}

      Stamp (const Stamp& s)
	: value (s.value), old_value (s.old_value), position (s.position)
      {}

      inline void set_value (type val)
      { old_value = value; value = val; }

      inline void set_position (position_type pos)
      { position = pos; }

      inline type get_value ()
      { return value; }

      inline type get_old_value ()
      { return old_value; }

      inline position_type get_position ()
      { return position; }
      
      bool operator== (const Stamp& stamp)
      {  return stamp.value == value; }

      bool operator< (const Stamp& stamp)
      { return value < stamp.value; }

      bool operator> (const Stamp& stamp)
      { return stamp.value < value; }
    };

    template <typename obj, typename cmp>
    class Scheduler
    {
    public:
      typedef typename dbc::BinHeap<obj,cmp>::min_iterator CurrentEvenIter;

    private:
      dbc::BinHeap<obj,cmp>	queue;

    public:
      inline CurrentEvenIter current_event_iterator ()
      { return queue.min_iter (); }

      inline void add_event (obj& e)
      { queue.add (e); }

      inline void add_event (obj e)
      { queue.add (e); }

      inline void start ()
      { queue.build (); }

      inline void next (obj& e)
      { queue.inc_priority (e); }

      inline void next (obj e)
      { queue.inc_priority (e); }

      inline void flush ()
      { queue.flush (); }
    };

    class StampableEntity : public dbc::PCEntity
    {
      ENTITY_DEF (StampableEntity);

    public:
      class Cmp;
      typedef BinHeap<StampableEntity, Cmp >::position_type position_type;
      typedef Stamp<double,  position_type> StampType;

    private:
      StampType	stamp;

    public:
      class Cmp
      {
      public:
	typedef StampableEntity::StampType PriorityType;

      public:
	static inline PriorityType get_priority (StampableEntity& s)
	{ return s.get_stamp (); }

	static inline PriorityType get_priority (StampableEntity::Ptr& s)
	{ return s->get_stamp (); }

	static inline position_type get_finger (StampableEntity& s)
	{ return s.get_stamp ().get_position (); }

	static inline position_type get_finger (StampableEntity::Ptr& s)
	{ return s->get_stamp ().get_position (); }

	static inline void set_finger (StampableEntity& s, position_type pos)
	{ s.get_stamp ().set_position (pos); }

	static inline void set_finger (StampableEntity::Ptr& s, position_type pos)
	{ s->get_stamp ().set_position (pos); }
      
	static inline bool less (StampableEntity& s1, StampableEntity& s2)
	{ return s1.get_stamp () < s2.get_stamp (); }

	static inline bool less (StampableEntity::Ptr& s1, StampableEntity::Ptr& s2)
	{ return s1->get_stamp () < s2->get_stamp (); }

	static inline bool less (PriorityType& v, StampableEntity& s2)
	{ return v < s2.get_stamp (); }

	static inline bool less (PriorityType& v, StampableEntity::Ptr& s2)
	{ return v < s2->get_stamp (); }

	static inline bool less (StampableEntity& s1, PriorityType& v)
	{ return v > s1.get_stamp (); }

	static inline bool less (StampableEntity::Ptr& s1, PriorityType& v)
	{ return v > s1->get_stamp (); }

	static inline void init (StampableEntity::Ptr& s)
	{ s.reset (); }
      };

    protected:
      StampableEntity (bool alloc_pc_containers)
	: PCEntity (alloc_pc_containers)
      {}

    public:
      StampableEntity ()
      {}

      inline StampType& get_stamp ()
      { return stamp; }

      inline void set_stamp (StampType& istamp)
      { stamp = istamp; }

      inline void set_stamp (double v)
      { stamp.set_value (v); }

      inline void update_stamp (double dv)
      { stamp.set_value (stamp.get_value () + dv); }
    };

  };
};

#endif
